package com.peke.hex.editor.utils;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.peke.hex.editor.bean.ByteBean;
import com.peke.hex.editor.bean.ModifiedData;
import com.peke.hex.editor.consts.ByteConst;

import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * bytes修改数据库帮助类
 * 用于存储修改bytes的历史记录
 */
public class BytesModifiedDbHelper {
    /**
     * 已修改的数据
     */
    private static final String TABLE_NAME_MODIFIED_BYTES = "modified_bytes";
    /**
     * 修改历史数据
     */
    private static final String TABLE_NAME_MODIFIED_HISTORY = "modified_history_bytes";

    /**
     * 文件片段大小
     */
    private static final long PART_SIZE = ByteConst.GB; // 1GB

    private final File dbFileDir;

    private File bytesModifiedDbFile;
    private SQLiteDatabase db;

    /**
     * 每GB大小分一个片段
     */
    private int partCount = 1;

    private BytesModifiedDbHelper(Context context){
        dbFileDir = new File(context.getFilesDir(),"FileDataWriter");
    }

    public BytesModifiedDbHelper(Context context,String name,long fileSize) {
        this(context);
        String dbFileName = "bytesModified"+ Md5Utils.getMD5(name.getBytes()) + ".db";
        bytesModifiedDbFile = new File(dbFileDir,dbFileName);
        if (bytesModifiedDbFile.exists()){
            bytesModifiedDbFile.delete();
        }
        try {
            File parentFile = bytesModifiedDbFile.getParentFile();
            if (!parentFile.exists())
                parentFile.mkdirs();
            bytesModifiedDbFile.createNewFile();
        } catch (Exception e) {
            e.printStackTrace();
        }

        //计算分片数量，需要向上取整
        partCount = (int) Math.ceil(fileSize * 1.0 / PART_SIZE);

        db = SQLiteDatabase.openDatabase(bytesModifiedDbFile.getAbsolutePath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
        createDataTable();
        createHistoryTable();

    }

    /**
     * 获取已保存的实例
     * @param name 名称
     */
    public static BytesModifiedDbHelper ofSaveInstance(Context context,String name,long fileSize){
        String dbFileName = "bytesModified"+ Md5Utils.getMD5(name.getBytes()) + ".db";
        File bytesModifiedDbFile = new File(new File(context.getFilesDir(),"FileDataWriter"),dbFileName);
        if (bytesModifiedDbFile.exists()){
            BytesModifiedDbHelper dbHelper = new BytesModifiedDbHelper(context);
            dbHelper.bytesModifiedDbFile = bytesModifiedDbFile;

            dbHelper.db = SQLiteDatabase.openDatabase(bytesModifiedDbFile.getAbsolutePath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
            dbHelper.createDataTable();
            dbHelper.createHistoryTable();

            return dbHelper;
        }
        else {
            return new BytesModifiedDbHelper(context,name,fileSize);
        }
    }

    /**
     * 判断是否有存档
     */
    public boolean hasSaveInstance(String name){
        String dbFileName = "bytesModified"+ Md5Utils.getMD5(name.getBytes()) + ".db";
        File bytesModifiedDbFile = new File(dbFileDir,dbFileName);
        return bytesModifiedDbFile.exists();
    }

    /**
     * 删除数据库
     */
    private void dropDatabase(){
        db.close();
        if (bytesModifiedDbFile.exists()){
            //删除数据库文件
            bytesModifiedDbFile.delete();
            //删除数据库日志文件
            File journalFile = new File(bytesModifiedDbFile.getAbsolutePath()+"-journal");
            if (journalFile.exists())
                journalFile.delete();
        }
    }

    /**
     * 创建历史记录表
     */
    private void createHistoryTable(){
        for (int part = 0; part < partCount; part++) {
            String tableName = TABLE_NAME_MODIFIED_HISTORY + "_" + part;
            String create_sql = "CREATE TABLE IF NOT EXISTS " + tableName
                    + "("
                    + "_pointer INTEGER," //历史记录指针
                    + "_index INTEGER," //数据索引地址
                    + "_last_byte INTEGER," //上一次数据
                    + "_history_byte INTEGER" //修改历史数据
                    + ");";
            db.execSQL(create_sql);
        }
    }

    /**
     * 创建数据表
     */
    private void createDataTable(){
        for (int part = 0; part < partCount; part++) {
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
            String create_sql = "CREATE TABLE IF NOT EXISTS " + tableName
                    + "("
                    + "_index INTEGER UNIQUE," //数据索引地址
                    + "_byte INTEGER DEFAULT (256)" //u8数据，大于等于256表示未修改
                    + ");";
            db.execSQL(create_sql);
        }
    }

    /**
     * 查询修改历史
     * @return List Pair&lt;修改数据,上一次数据&gt;
     */
    public List<ModifiedData> queryModifiedHistory(int pointer){
        List<ModifiedData> bytes = new ArrayList<>();
        for (int part = 0; part < partCount; part++) {
            String tableName = TABLE_NAME_MODIFIED_HISTORY + "_" + part;
            String sql = "SELECT * FROM " + tableName + " WHERE _pointer=? ORDER BY _index ASC";
            Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(pointer)});
            while (cursor.moveToNext()){
                int _history_byte = cursor.getInt(cursor.getColumnIndex("_history_byte"));
                int _last_byte = cursor.getInt(cursor.getColumnIndex("_last_byte"));
                bytes.add(new ModifiedData(_history_byte,_last_byte));
            }
            IOUtils.closeQuietly(cursor);
        }
        return bytes;
    }

    /**
     * 删除修改的数据
     */
    public void deleteModifiedData(long startFileIndex,long length){
        long endFileIndex = startFileIndex + length;
        IndexWrapper startIndex = parseFileIndex(startFileIndex);
        IndexWrapper endIndex = parseFileIndex(endFileIndex);

        if (startIndex.part == endIndex.part){
            //同一分片
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + startIndex.part;
            String sql = "DELETE FROM " + tableName + " WHERE _index>=? AND _index<?";
            String[] whereArgs = new String[]{String.valueOf(startIndex.index),String.valueOf(endIndex.index)};
            db.execSQL(sql,whereArgs);
        }
        else {
            //不同分片
            for (int part = startIndex.part; part <= endIndex.part; part++) {
                String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
                String sql = "DELETE FROM " + tableName;
                String whereClause;
                String[] whereArgs;
                if (part == startIndex.part){
                    //第一分片
                    whereClause = "_index>=?";
                    whereArgs = new String[]{String.valueOf(startIndex.index)};
                }
                else if (part == endIndex.part){
                    //最后一分片
                    whereClause = "_index<?";
                    whereArgs = new String[]{String.valueOf(endIndex.index)};
                }
                else {
                    //中间分片
                    whereClause = null;
                    whereArgs = null;
                }

                if (whereClause != null){
                    sql += " WHERE " + whereClause;
                    db.execSQL(sql,whereArgs);
                }
                else {
                    db.execSQL(sql);
                }

            }

        }


    }

    /**
     * 删除未修改的byte数据
     */
    public void deleteUnmodifiedData(){
        //删除大于等于当前指针的历史数据
        for (int part = 0; part < partCount; part++) {
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
            db.delete(tableName,"_byte>=?",new String[]{String.valueOf(256)});
        }
    }

    /**
     * 添加历史数据
     * @param pointer 指针位置
     * @param fileIndex 数据位置
     * @param bytes 写入的数据
     * @param lastBytes 最后修改的数据（当前页面数据）
     */
    public void insertHistory(int pointer, long fileIndex, byte[] bytes, ByteBean[] lastBytes) {
        //删除大于等于当前指针的历史数据
        for (int part = 0; part < partCount; part++) {
            String tableName = TABLE_NAME_MODIFIED_HISTORY + "_" + part;
            db.delete(tableName,"_pointer>=?",new String[]{String.valueOf(pointer)});
        }

        IndexWrapper startIndex = parseFileIndex(fileIndex);
        IndexWrapper endIndex = parseFileIndex(fileIndex + bytes.length);

        //插入数据
        for (int part = startIndex.part; part <= endIndex.part; part++) {
            String tableName = TABLE_NAME_MODIFIED_HISTORY + "_" + part;
            for (int i=startIndex.index,j=0;j<bytes.length;i++,j++){
                ContentValues contentValues = new ContentValues();
                contentValues.put("_pointer",pointer);
                contentValues.put("_index",i);
                contentValues.put("_history_byte",bytes[j]);
                contentValues.put("_last_byte",lastBytes[j].value);
                db.insertWithOnConflict(tableName,null,contentValues,SQLiteDatabase.CONFLICT_REPLACE);
            }
        }

    }

    /**
     * 写入修改的数据到数据库
     * @param fileIndex 数据位置
     * @param modifiedBytes 修改的数据
     * @param orgBytes 原始数据
     */
    public void insertBytesToDb(long fileIndex, byte[] modifiedBytes, byte[] orgBytes){
        IndexWrapper startIndex = parseFileIndex(fileIndex);
        IndexWrapper endIndex = parseFileIndex(fileIndex + modifiedBytes.length);

        for (int part = startIndex.part; part <= endIndex.part; part++) {
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
            for (int i=startIndex.index,j=0;j<modifiedBytes.length;i++,j++){
                int value = modifiedBytes[j] == orgBytes[j] ? 256 : modifiedBytes[j];
                ContentValues contentValues = new ContentValues();
                contentValues.put("_index",i);
                contentValues.put("_byte",value);
                db.insertWithOnConflict(tableName,null,contentValues,SQLiteDatabase.CONFLICT_REPLACE);
            }
        }
    }

    /**
     * 读取历史数据写入到修改数据的数据库
     * @param isUndo true:undo，false:redo
     */
    public void insertHistoryToDb(long fileIndex, List<ModifiedData> historyBytes,byte[] orgBytes,boolean isUndo){
        IndexWrapper startIndex = parseFileIndex(fileIndex);
        IndexWrapper endIndex = parseFileIndex(fileIndex + historyBytes.size());

        for (int part = startIndex.part; part <= endIndex.part; part++) {
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
            for (int i=startIndex.index,j=0;j<historyBytes.size();i++,j++){
                ModifiedData data = historyBytes.get(j);
                int value = isUndo ? data.last : data.modified;
                if (value == orgBytes[j])
                    value = 256;
                ContentValues contentValues = new ContentValues();
                contentValues.put("_index",i);
                contentValues.put("_byte",value);
                db.insertWithOnConflict(tableName,null,contentValues,SQLiteDatabase.CONFLICT_REPLACE);
            }
        }
    }

    /**
     * 查询已修改的数据
     * @param orgData 原始数据
     */
    public void queryModifiedByteBeans(long fileIndex, ByteBean[] orgData){
        IndexWrapper startIndex = parseFileIndex(fileIndex);
        IndexWrapper endIndex = parseFileIndex(fileIndex + orgData.length);

        if (startIndex.part == endIndex.part){
            //同一分片
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + startIndex.part;
            String sql = "SELECT * FROM "+tableName + " WHERE _index>=? AND _index<? ORDER BY _index ASC";
            Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(startIndex.index), String.valueOf(endIndex.index)});

            while (cursor.moveToNext()){
                int _index = cursor.getInt(cursor.getColumnIndex("_index"));
                int _byte = cursor.getInt(cursor.getColumnIndex("_byte"));
                if (_byte >= 256) {
                    continue;
                }

                IndexWrapper dbIndex = parseDbIndex(_index,startIndex.part);
                int dataIndex = (int) (dbIndex.fileIndex - fileIndex);
                if (dataIndex >= orgData.length) {
                    break;
                }

                ByteBean bean = orgData[dataIndex];
                bean.modified = bean.value != _byte;
                if (bean.modified) {
                    bean.value = (byte) _byte;
                }
            }

            IOUtils.closeQuietly(cursor);
        }
        else {
            //不同分片
            for (int part = startIndex.part; part <= endIndex.part; part++) {
                Cursor cursor = null;
                String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
                if (part == startIndex.part){
                    //第一分片
                    String sql = "SELECT * FROM "+tableName + " WHERE _index>=? ORDER BY _index ASC";
                    cursor = db.rawQuery(sql, new String[]{String.valueOf(startIndex.index)});
                }
                else if (part == endIndex.part){
                    //最后一分片
                    String sql = "SELECT * FROM "+tableName + " WHERE _index<? ORDER BY _index ASC";
                    cursor = db.rawQuery(sql, new String[]{String.valueOf(endIndex.index)});
                }
                else {
                    //中间分片
                    String sql = "SELECT * FROM "+tableName + " ORDER BY _index ASC";
                    cursor = db.rawQuery(sql, null);
                }

                while (cursor.moveToNext()){
                    int _index = cursor.getInt(cursor.getColumnIndex("_index"));
                    int _byte = cursor.getInt(cursor.getColumnIndex("_byte"));
                    if (_byte >= 256) {
                        continue;
                    }

                    IndexWrapper dbIndex = parseDbIndex(_index,part);
                    int dataIndex = (int) (dbIndex.fileIndex - fileIndex);
                    if (dataIndex >= orgData.length) {
                        break;
                    }

                    ByteBean bean = orgData[dataIndex];
                    bean.modified = bean.value != _byte;
                    if (bean.modified) {
                        bean.value = (byte) _byte;
                    }
                }

                IOUtils.closeQuietly(cursor);
            }

        }

    }

    public void queryModifiedBytes(long fileIndex, byte[] orgData){
        IndexWrapper startIndex = parseFileIndex(fileIndex);
        IndexWrapper endIndex = parseFileIndex(fileIndex + orgData.length);

        if (startIndex.part == endIndex.part){
            //同一分片
            String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + startIndex.part;
            String sql = "SELECT * FROM "+tableName + " WHERE _index>=? AND _index<? ORDER BY _index ASC";
            Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(startIndex.index), String.valueOf(endIndex.index)});

            while (cursor.moveToNext()){
                int _index = cursor.getInt(cursor.getColumnIndex("_index"));
                int _byte = cursor.getInt(cursor.getColumnIndex("_byte"));
                if (_byte >= 256) {
                    continue;
                }

                IndexWrapper dbIndex = parseDbIndex(_index,startIndex.part);
                int dataIndex = (int) (dbIndex.fileIndex - fileIndex);
                if (dataIndex >= orgData.length) {
                    break;
                }

                orgData[dataIndex] = (byte) _byte;
            }
            IOUtils.closeQuietly(cursor);
        }
        else {
            //不同分片
            for (int part = startIndex.part; part <= endIndex.part; part++) {
                Cursor cursor = null;
                String tableName = TABLE_NAME_MODIFIED_BYTES + "_" + part;
                if (part == startIndex.part){
                    //第一分片
                    String sql = "SELECT * FROM "+tableName + " WHERE _index>=? ORDER BY _index ASC";
                    cursor = db.rawQuery(sql, new String[]{String.valueOf(startIndex.index)});
                }
                else if (part == endIndex.part){
                    //最后一分片
                    String sql = "SELECT * FROM "+tableName + " WHERE _index<? ORDER BY _index ASC";
                    cursor = db.rawQuery(sql, new String[]{String.valueOf(endIndex.index)});
                }
                else {
                    //中间分片
                    String sql = "SELECT * FROM "+tableName + " ORDER BY _index ASC";
                    cursor = db.rawQuery(sql, null);
                }

                while (cursor.moveToNext()){
                    int _index = cursor.getInt(cursor.getColumnIndex("_index"));
                    int _byte = cursor.getInt(cursor.getColumnIndex("_byte"));
                    if (_byte >= 256) {
                        continue;
                    }

                    IndexWrapper dbIndex = parseDbIndex(_index,part);
                    int dataIndex = (int) (dbIndex.fileIndex - fileIndex);
                    if (dataIndex >= orgData.length) {
                        break;
                    }

                    orgData[dataIndex] = (byte) _byte;
                }

                IOUtils.closeQuietly(cursor);
            }
        }

    }

    private IndexWrapper parseFileIndex(long fileIndex){
        IndexWrapper wrapper = new IndexWrapper();
        wrapper.fileIndex = fileIndex;
        wrapper.part = new BigDecimal(fileIndex / PART_SIZE).intValue();
        wrapper.index = new BigDecimal(fileIndex % PART_SIZE).intValue();
        return wrapper;
    }

    private IndexWrapper parseDbIndex(int index,int part){
        IndexWrapper wrapper = new IndexWrapper();
        wrapper.part = part;
        wrapper.index = index;
        wrapper.fileIndex = part * PART_SIZE + index;
        return wrapper;
    }

    public void close(){
        dropDatabase();
    }

    private static class IndexWrapper {
        int part = 0;
        int index = 0;
        long fileIndex = 0;
    }

}